##
# This module requires Metasploit: http://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

require 'msf/core'

class Metasploit3 < Msf::Auxiliary

  include Msf::Exploit::Remote::Tcp
  include Msf::Auxiliary::Scanner
  include Msf::Auxiliary::Report

  def initialize
    super(
      'Name'        => 'Java RMI Server Insecure Endpoint Code Execution Scanner',
      'Description' => 'Detect Java RMI endpoints',
      'Author'     => ['mihi', 'hdm'],
      'License'     => MSF_LICENSE,
      'References'     =>
        [
          # RMI protocol specification
          [ 'URL', 'http://download.oracle.com/javase/1.3/docs/guide/rmi/spec/rmi-protocol.html'],
          # Placeholder reference for matching
          [ 'MSF', 'java_rmi_server']
        ],
      'DisclosureDate' => 'Oct 15 2011'
    )

    register_options(
      [
        Opt::RPORT(1099)
      ], self.class)
  end

  def setup
    buf = gen_rmi_loader_packet

    jar = Rex::Text.rand_text_alpha(rand(8)+1) + '.jar'
    old_url = "file:./rmidummy.jar"
    new_url = "file:RMIClassLoaderSecurityTest/" + jar

    # Java strings in serialized data are prefixed with a 2-byte, big endian length
    # (at least, as long as they are shorter than 65536 bytes)
    find_me = [old_url.length].pack("n") + old_url

    idx = buf.index(find_me)
    len = [new_url.length].pack("n")

    # Now replace it with the new url
    buf[idx, find_me.length] = len + new_url

    @pkt = "JRMI" + [2,0x4b,0,0].pack("nCnN") + buf
  end

  def run_host(target_host)

    begin
      connect
      sock.put("\x4a\x52\x4d\x49\0\x02\x4b")
      res = sock.get_once
      disconnect

      if res and res =~ /^\x4e..([^\x00]+)\x00\x00/
        info = $1

        begin
          # Determine if the instance allows remote class loading
          connect
          sock.put(@pkt) rescue nil

          buf = ""
          1.upto(6) do
            res = sock.get_once(-1, 5) rescue nil
            break if not res
            buf << res
          end

        rescue ::Interrupt
          raise $!
        rescue ::Exception
        ensure
          disconnect
        end

        if buf =~ /RMI class loader disabled/
          print_status("#{rhost}:#{rport} Java RMI Endpoint Detected: Class Loader Disabled")
          report_service(:host => rhost, :port => rport, :name => "java-rmi", :info => "Class Loader: Disabled")
        elsif buf.length > 0
          print_good("#{rhost}:#{rport} Java RMI Endpoint Detected: Class Loader Enabled")
          svc = report_service(:host => rhost, :port => rport, :name => "java-rmi", :info => "Class Loader: Enabled")
          report_vuln(
            :host         => rhost,
            :service      => svc,
            :name         => self.name,
            :info         => "Module #{self.fullname} confirmed remote code execution via this RMI service",
            :refs         => self.references
          )
        else
          print_status("#{rhost}:#{rport} Java RMI Endpoint Detected")
          report_service(:host => rhost, :port => rport, :name => "java-rmi", :info => "")
        end

      end

    rescue ::Interrupt
      raise $!
    rescue ::Rex::ConnectionError, ::IOError
    ensure
      disconnect
    end

  end

  def gen_rmi_loader_packet
    "\x50\xac\xed\x00\x05\x77\x22\x00\x00\x00\x00\x00\x00\x00\x02\x00" +
    "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +
    "\x00\xf6\xb6\x89\x8d\x8b\xf2\x86\x43\x75\x72\x00\x18\x5b\x4c\x6a" +
    "\x61\x76\x61\x2e\x72\x6d\x69\x2e\x73\x65\x72\x76\x65\x72\x2e\x4f" +
    "\x62\x6a\x49\x44\x3b\x87\x13\x00\xb8\xd0\x2c\x64\x7e\x02\x00\x00" +
    "\x70\x78\x70\x00\x00\x00\x00\x77\x08\x00\x00\x00\x00\x00\x00\x00" +
    "\x00\x73\x72\x00\x14\x6d\x65\x74\x61\x73\x70\x6c\x6f\x69\x74\x2e" +
    "\x52\x4d\x49\x4c\x6f\x61\x64\x65\x72\xa1\x65\x44\xba\x26\xf9\xc2" +
    "\xf4\x02\x00\x00\x74\x00\x13\x66\x69\x6c\x65\x3a\x2e\x2f\x72\x6d" +
    "\x69\x64\x75\x6d\x6d\x79\x2e\x6a\x61\x72\x78\x70\x77\x01\x00\x0a"
  end

end
